import { mat4, vec3 } from 'gl-matrix'
import * as utils from '../utils'

function createCourse31 (gl: WebGLRenderingContext, dom: HTMLCanvasElement) {
  gl.enable(gl.DEPTH_TEST)
  gl.clearColor(0.0, 0.0, 0.0, 1.0)
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

  const vectexShaderSrc = `
    uniform mat4 u_MvpMatrix;
    uniform mat4 u_NormalMatrix;
    uniform mat4 u_ModelMatrix;
    attribute vec4 a_Position;
    attribute vec4 a_Color;
    attribute vec4 a_Normal;
    varying vec4 v_Color;
    varying vec3 v_Normal;
    varying vec4 v_Position;
    void main() {
        v_Normal = normalize(vec3(u_NormalMatrix * a_Normal));
        v_Position = u_ModelMatrix * a_Position;
        v_Color = a_Color;
        gl_Position = u_MvpMatrix * a_Position;
    }
  `

  const fragmentShaderSrc = `
  precision mediump float;
  uniform vec3 u_LightPosition;
  uniform vec3 u_LightColor;
  uniform vec3 u_AmbientLight;
  varying vec4 v_Color;
  varying vec3 v_Normal;
  varying vec4 v_Position;
    void main() {
        vec3 lightDirection = normalize(u_LightPosition - vec3(v_Position));
        float nDotl = max(dot(lightDirection, normalize(v_Normal)), 0.0);
        vec3 diffuse = u_LightColor *  v_Color.rgb * nDotl;
        vec3 ambient = u_AmbientLight * v_Color.rgb;
        gl_FragColor = vec4(diffuse + ambient, v_Color.a);
    }
  `

  const program = utils.initShaders(gl, vectexShaderSrc, fragmentShaderSrc)

  const vertics = new Float32Array([
    -0.2, -0.2, +0.2, +0.2, -0.2, +0.2, +0.2, +0.2, +0.2, -0.2, +0.2, +0.2,
    +0.2, -0.2, -0.2, -0.2, -0.2, -0.2, -0.2, +0.2, -0.2, +0.2, +0.2, -0.2,

    -0.2, -0.2, -0.2, -0.2, -0.2, +0.2, -0.2, +0.2, +0.2, -0.2, +0.2, -0.2,
    +0.2, -0.2, +0.2, +0.2, -0.2, -0.2, +0.2, +0.2, -0.2, +0.2, +0.2, +0.2,

    -0.2, +0.2, +0.2, +0.2, +0.2, +0.2, +0.2, +0.2, -0.2, -0.2, +0.2, -0.2,
    -0.2, -0.2, -0.2, +0.2, -0.2, -0.2, +0.2, -0.2, +0.2, -0.2, -0.2, +0.2
  ])

  const colors = new Float32Array([
    1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0,
    1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0,
    1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0,
    1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0,
    1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0,
    1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0
  ])

  const normals = new Float32Array([
    0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0,
    0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0,
    -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0,
    1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0,
    0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0,
    0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0
  ])

  const indeics = new Uint8Array([
    0, 1, 2, 0, 2, 3,
    4, 5, 6, 4, 6, 7,

    8, 9, 10, 8, 10, 11,
    12, 13, 14, 12, 14, 15,

    16, 17, 18, 16, 18, 19,
    20, 21, 22, 20, 22, 23
  ])

  const verticsBuffer = gl.createBuffer()
  gl.bindBuffer(gl.ARRAY_BUFFER, verticsBuffer)
  gl.bufferData(gl.ARRAY_BUFFER, vertics, gl.STATIC_DRAW)
  const vertorSize = vertics.BYTES_PER_ELEMENT
  const aPosition = gl.getAttribLocation(program, 'a_Position')
  gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, vertorSize * 3, 0)
  gl.enableVertexAttribArray(aPosition)

  const colorBuffer = gl.createBuffer()
  gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer)
  gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW)
  const colorSize = colors.BYTES_PER_ELEMENT
  const aColor = gl.getAttribLocation(program, 'a_Color')
  gl.vertexAttribPointer(aColor, 3, gl.FLOAT, false, colorSize * 3, 0)
  gl.enableVertexAttribArray(aColor)

  const normalBuffer = gl.createBuffer()
  gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer)
  gl.bufferData(gl.ARRAY_BUFFER, normals, gl.STATIC_DRAW)
  const normalSize = normals.BYTES_PER_ELEMENT
  const aNormal = gl.getAttribLocation(program, 'a_Normal')
  gl.vertexAttribPointer(aNormal, 3, gl.FLOAT, false, normalSize * 3, 0)
  gl.enableVertexAttribArray(aNormal)

  const indexBuffer = gl.createBuffer()
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer)
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indeics, gl.STATIC_DRAW)

  const modelMatrix = mat4.create()
  const axis = vec3.create()
  vec3.set(axis, 1.0, 1.0, 1.0)
  mat4.fromRotation(modelMatrix, 0.0, axis)
  const uModelMatrix = gl.getUniformLocation(program, 'u_ModelMatrix')
  gl.uniformMatrix4fv(uModelMatrix, false, modelMatrix)

  const viewMatrix = mat4.create()
  const eye = vec3.create()
  vec3.set(eye, 1, 1, 1)
  const center = vec3.create()
  vec3.set(center, 0.0, 0.0, 0.0)
  const up = vec3.create()
  vec3.set(up, 0.0, 1.0, 0.0)
  mat4.lookAt(viewMatrix, eye, center, up)

  const projMatrix = mat4.create()
  mat4.perspective(projMatrix, 70, dom.offsetWidth / dom.offsetHeight, 0.01, 10)

  const viewModelMatrix = mat4.create()
  mat4.multiply(viewModelMatrix, viewMatrix, modelMatrix)

  const mvpMatrix = mat4.create()
  mat4.multiply(mvpMatrix, projMatrix, viewModelMatrix)

  const uMvpMatrix = gl.getUniformLocation(program, 'u_MvpMatrix')
  gl.uniformMatrix4fv(uMvpMatrix, false, mvpMatrix)

  gl.drawElements(gl.TRIANGLES, indeics.length, gl.UNSIGNED_BYTE, 0)

  // 法线变换
  const normalMatrix = mat4.create()
  mat4.invert(normalMatrix, modelMatrix)
  mat4.transpose(normalMatrix, normalMatrix)
  const uNormalMatrix = gl.getUniformLocation(program, 'u_NormalMatrix')
  gl.uniformMatrix4fv(uNormalMatrix, false, normalMatrix)

  // 点光源位置
  const spotLightPosition = vec3.create()
  vec3.set(spotLightPosition, 0, 3, 4)
  const spotLightColor = vec3.create()
  vec3.set(spotLightColor, 1.0, 1.0, 1.0)
  const uLightPosition = gl.getUniformLocation(program, 'u_LightPosition')
  gl.uniform3fv(uLightPosition, spotLightPosition)
  const uLightColor = gl.getUniformLocation(program, 'u_LightColor')
  gl.uniform3fv(uLightColor, spotLightColor)

  // 环境光颜色
  const ambientLight = vec3.create()
  vec3.set(ambientLight, 0.0, 1.0, 1.0)
  const uAmbientLight = gl.getUniformLocation(program, 'u_AmbientLight')
  gl.uniform3fv(uAmbientLight, ambientLight)

  let oldTime = Date.now()
  let angle = 0
  animation()
  function animation () {
    const newTime = Date.now()
    const delta = (newTime - oldTime) / 1000 * 30
    angle += delta
    angle = angle % 360
    oldTime = newTime
    mat4.fromRotation(modelMatrix, angle / 180 * Math.PI, axis)
    mat4.multiply(viewModelMatrix, viewMatrix, modelMatrix)
    mat4.multiply(mvpMatrix, projMatrix, viewModelMatrix)
    gl.uniformMatrix4fv(uMvpMatrix, false, mvpMatrix)

    mat4.invert(normalMatrix, modelMatrix)
    mat4.transpose(normalMatrix, normalMatrix)
    gl.uniformMatrix4fv(uNormalMatrix, false, normalMatrix)

    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
    gl.drawElements(gl.TRIANGLES, indeics.length, gl.UNSIGNED_BYTE, 0)

    requestAnimationFrame(animation)
  }
}
export {
  createCourse31
}
